/*
 * servidor.c
 *
 *  Created on: 21/07/2014
 *      Author: migue
 */

#include "servidor_interno.h"
#include "mapa.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#define ID_ADMIN				10
#define ID_BASE_PUERTAS			100
#define ID_BASE_BUSES			200
#define ID_BASE_BUSES_REP		300
#define ID_BASE_BROKERS			400
#define ID_BASE_SALAS			500

#define ID_BUS_RESPUESTO_GRL	299

#define ID_BASE_PERSONAS		1000

#define ID_ERROR 				-500000000


int idBusReemplazo(int numero) {
	return ID_BASE_BUSES_REP + numero;
}

int idEntidad(DatosEntidad* ident) {


	int res, aux , aux2;

	if (ident->entidad == TIPO_PUERTA_ENT || ident->entidad == TIPO_PUERTA_SAL) {

		if (ident->entidad == TIPO_PUERTA_ENT) {
			aux = 0;
		}else if (ident->entidad == TIPO_PUERTA_SAL) {
			aux = 1;
		}
		else {
			aux = ID_ERROR;
		}

		res = ID_BASE_PUERTAS + 2 * ident->numeroEntidad + aux;
	}
	else if (ident->entidad == TIPO_SALA_COLA || ident->entidad == TIPO_SALA_ENT ||
			ident->entidad == TIPO_SALA_SAL) {

		if (ident->entidad == TIPO_SALA_COLA) {
			aux = 0;
		}else if (ident->entidad == TIPO_SALA_ENT ) {
			aux = 1;
		}else if (ident->entidad == TIPO_SALA_SAL) {
			aux = 2;
		}
		else {
			aux = ID_ERROR;
		}


		if (ident->lugar == LUGAR_ENTRADA) {
			aux2 = 0;
		}else if (ident->lugar == LUGAR_ZOO) {
			aux2 = 1;
		}
		else{
			aux2 = ID_ERROR;
		}


		res = ID_BASE_SALAS + 10 * ident->numeroEntidad + aux2 * 3 + aux  ;

	}
	else if (ident->entidad == TIPO_BUS && ident->metodo == MTD_ID_BUS_REEMPLAZO) {
		res = ID_BASE_BUSES_REP + ident->numeroEntidad;
	}
	else if (ident->entidad == TIPO_BUS) {
		res = ID_BASE_BUSES + ident->numeroEntidad;
	}
	else if (ident->entidad == TIPO_BROKER) {
		res = ID_BASE_BROKERS + ident->numeroEntidad;
	}
	else if(ident->entidad == TIPO_ADMIN) {
		res = ID_ADMIN;
	}
	else {
		res = 0;
	}
	printf("\t - consultanto id: %i \n", res);

	return res;
}

void copiarDatos(IdentificacionEntidad* destino, DATOS_NODO* datos) {
	destino->id_entidad = datos->id;
	destino->id_brokerConectado = datos->idBrokerConectado;

	copiarDireccion(&destino->dir_conexion, &datos->ip, sizeof(DireccionHost));

	//destino->dir_conexion = datos->ip;
	destino->puerto_conexion = datos->puerto;
	destino->timeout_anillo = datos->timeout_anillo;
}

void agregarEntidad(MAPA_DATOS* mapa,int id, DatosEntidad* ent, DATOS_NODO* datos ) {
	printf("\t - agregando entidad nueva \n");

	if (ent->entidad == TIPO_PERSONA) {
		datos->id = mapa->_cantPersonas++ + ID_BASE_PERSONAS;
	}
	else {
		datos->id = id;
	}

	copiarDireccion(&datos->ip, &ent->direccion, sizeof(DireccionHost));
	datos->tipo = ent->entidad;
	datos->estadoBroker = BROKER_ACTIVO;

	if (ent->entidad == TIPO_BROKER) {
		mapa->_cantBrokers++;
		datos->idBrokerConectado = id;
		datos->puerto = ent->puerto_escucha;

		datos->timeout_anillo = mapa->_ultimoTimeout;
		mapa->_ultimoTimeout += DELTA_TIMEOUT;

		agregarNodoBroker(mapa, crearNodoBroker(datos));
	}
	else {

		if (mapa->_cantBrokers > 0) {

			DATOS_NODO* datosBroker = mapa->_ultBroker->datos;

			if (datosBroker == NULL) {
				printf("ERROR FATAL no se encontro broker registrado");
				exit(1);
			}

			datos->idBrokerConectado = datosBroker->id;
			copiarDireccion(&datos->ip, &datosBroker->ip, sizeof(DireccionHost));
			datos->puerto = datosBroker->puerto;


			pasarSiguienteBroker(mapa);


		}
		else {
			datos->idBrokerConectado = ID_ERROR;
			printf("ERROR aun no se registro ningun BROKER \n");
		}

	}

	struct sockaddr_in *sock = (struct sockaddr_in*) &datos->ip;

	printf("\t\t id : %i \n", datos->id);
	printf("\t\t id broker asignado: %i \n", datos->idBrokerConectado);
	printf("\t\t ip broker: %s \n", inet_ntoa( sock->sin_addr ));

	insertarNodo(mapa, datos);

	if (ent->entidad == TIPO_PERSONA)
		insertarPersona(mapa, datos, ent->id_auxiliar);
}

void identificarEntidad(DatosEntidad* identEnt,IdentificacionEntidad *resp) {

	struct sockaddr_in* sA = (struct sockaddr_in *) &identEnt->direccion;

	printf("Peticion num_entidad: %i; tipo_ent: %i \n", identEnt->numeroEntidad, (int) identEnt->entidad);
	printf("\t ip origen: %s\n", inet_ntoa(sA->sin_addr));
	printf("\t pid origen: %i\n", identEnt->pid);

	static MAPA_DATOS* mapa = NULL;

	if (mapa == NULL)
		mapa = inicializarMapa();


	int id = idEntidad(identEnt);

	DATOS_NODO* datos = NULL;


	// if agregado para evitar repetir ids
	if (identEnt->entidad == TIPO_PERSONA && identEnt->metodo == MTD_OBTENER_ID) {
		datos = crearNodoDatos();
		agregarEntidad(mapa,0, identEnt, datos);
		copiarDatos(resp, datos);
	}

	else if (identEnt->metodo == MTD_OBTENER_ID || identEnt->metodo == MTD_CONSULTAR_ID) {

		if (identEnt->entidad == TIPO_PERSONA)
			datos = buscarPersona(mapa, identEnt->id_auxiliar, &identEnt->direccion);
		else
			datos = buscarNodo(mapa, id);

		if ( datos != NULL ) {
			if (identEnt->entidad == TIPO_BROKER && datos->estadoBroker == BROKER_MUERTO) {
				rehabilitarBroker(datos, identEnt);
				mapa->_cantBrokers++;
				agregarNodoBroker(mapa, crearNodoBroker(datos));
			}

			copiarDatos(resp, datos);
		}
		else if (identEnt->metodo == MTD_OBTENER_ID) {
			datos = crearNodoDatos();
			agregarEntidad(mapa,id, identEnt, datos);
			copiarDatos(resp, datos);
		}
		else {
			printf("\t **ERROR peticion no realizada, id_respuesta: -8888\n");
			resp->id_entidad = -8888;
			//exit(1);
		}

	}
	else if (identEnt->metodo == MTD_ID_BUS_REEMPLAZO){
		/**
		 * El BUS de repuesto pide el ID de repuesto GENERAL y se lo guarda en id_auxiliar
		 * dandole ademas un id (en id_entidad) para que se comunique con el BUS
		 *
		 *
		 */

		if (identEnt->numeroEntidad == -1) {
			id  = idBusReemplazo(++mapa->_cantBusReemp);
			datos = buscarNodo(mapa, id);

			if (mapa->_cantBusReemp >= 99) {
				mapa->_cantBusReemp = 0;
			}

		}


		if (datos == NULL && identEnt->numeroEntidad == -1) {
			datos = crearNodoDatos();
			agregarEntidad(mapa, id, identEnt, datos);
		}

		if (datos != NULL)
			copiarDatos(resp, datos);

		resp->id_auxiliar = ID_BUS_RESPUESTO_GRL;
	}
	else if (identEnt->metodo == MTD_REEMPLAZAR_BUS) {
		/**
		 * Reemplaza los datos del BUS Original el ROTO con el de respuesto
		 *
		 * ident->num_entidad : numero del BUS roto
		 *
		 * ident->id_auxiliar : id del Bus de repuesto
		 *
		 * ident->entidad : TIPO_BUS; (si o si tiene que ser de este tipo)
		 *
		 */

		// el id se calculo al principio
		DATOS_NODO* datosBusRoto = buscarNodo(mapa, id);
		DATOS_NODO* datosBusReemp = buscarNodo(mapa, identEnt->id_auxiliar);

		if (datosBusReemp == NULL || datosBusRoto == NULL) {
			printf("ERROR al querer reemplazar BUS, no se encontraron datos \n");
			return;
		}


		struct sockaddr_in* sA = (struct sockaddr_in *) &datosBusRoto->ip;

		printf("\t - REEMPLAZANDO DATOS DE BUS ROTO con id: %i\n", datosBusRoto->id);
		printf("\t - id broker viejo: %i\n",datosBusRoto->idBrokerConectado);
		printf("\t - ip del broker viejo: %s\n", inet_ntoa(sA->sin_addr));


//		datosBusRoto->idBrokerConectado = datosBusReemp->idBrokerConectado;
//		copiarDireccion(&datosBusRoto->ip, &datosBusReemp->ip, sizeof(DireccionHost));

		eliminarNodo(mapa, datosBusReemp->id);


		sA = (struct sockaddr_in *) &datosBusRoto->ip;

		printf("\t - id broker nuevo: %i\n",datosBusRoto->idBrokerConectado);
		printf("\t - ip del broker nuevo: %s\n", inet_ntoa(sA->sin_addr));


		copiarDatos(resp, datosBusRoto);

	}
	else if (identEnt->metodo == MTD_CONSULTAR_DATOS) {


		printf("\t - consultando datos por id : %i\n", identEnt->id_auxiliar);

		datos = buscarNodo(mapa, identEnt->id_auxiliar);

		if (datos != NULL) {
			copiarDatos(resp, datos);
		}
		else {
			resp->id_entidad = -555;
			resp->id_brokerConectado = -555;
			resp->id_auxiliar = -555;
		}

	}
	else if (identEnt->metodo == MTD_BROKER_CAIDO) {
		printf("\t informando broker caido, idBroker: %i\n", identEnt->id_auxiliar);
		printf("\t id de persona(num_entidad): %i\n",identEnt->numeroEntidad);

		DATOS_NODO* datosBroker;

		datosBroker = buscarNodo(mapa, identEnt->id_auxiliar);
		datos = buscarNodo(mapa, identEnt->numeroEntidad);

		if (datosBroker != NULL && datos != NULL) {

			if (datosBroker->estadoBroker == BROKER_ACTIVO) {
				inhabilitarBroker(mapa, datosBroker->id);
			}

			datosBroker = mapa->_ultBroker->datos;

			datos->idBrokerConectado = datosBroker->id;
			copiarDireccion(&datos->ip, &datosBroker->ip, sizeof(datos->ip));
			datos->puerto = datosBroker->puerto;

			copiarDatos(resp, datos);

			printf("\t - Informando Broker(id: %i); por cliente id: %i, nuevo broker: %i\n",
					identEnt->id_auxiliar, datos->id, datos->idBrokerConectado);

		}
		else {
			printf("\t - Error: No se encontro broker caido(id-%i) o persona(id-%i)\n", identEnt->id_auxiliar, identEnt->numeroEntidad);
		}

	}


	//sleep(2);
	//imprimirMapa(mapa);
	printf("\n\n");
}


void testServidor(void (*funcion)(DatosEntidad*, RespuestaRPC**)) {
	int errores = 0;

	DatosEntidad ident;
	RespuestaRPC r;
	RespuestaRPC *resp;
	resp = &r;

	/**
	 * Registro al Broker
	 */

	ident.entidad = TIPO_BROKER;
	ident.numeroEntidad = 1;
	ident.metodo = MTD_OBTENER_ID;

	(*funcion)(&ident, &resp);

	int idBroker = resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de broker registrado : %i \n", idBroker);


	/**
	 * Registro al Broker
	 */

	ident.entidad = TIPO_BROKER;
	ident.numeroEntidad = 2;
	ident.metodo = MTD_OBTENER_ID;

	(*funcion)(&ident, &resp);

	int idBroker2 = resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de broker registrado : %i \n", idBroker2);


	/**
	 * Registro una sala de entrada
	 */

	ident.entidad = TIPO_SALA_ENT;
	ident.numeroEntidad = 1;
	ident.metodo = MTD_OBTENER_ID;

	(*funcion)(&ident, &resp);

	int idSala = resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de Sala: %i; idBrokerConectado: %i \n", idSala, resp->RespuestaRPC_u.datos.id_brokerConectado);


	/*******************************************************************/


	ident.entidad = TIPO_BUS;
	ident.numeroEntidad = 1;
	ident.metodo = MTD_OBTENER_ID;

	(*funcion)(&ident, &resp);

	int idBUS =resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de BUS: %i ; idBrokerConectado: %i \n", idBUS, resp->RespuestaRPC_u.datos.id_brokerConectado);


	/*******************************************************************/


	ident.entidad = TIPO_PERSONA;
	ident.id_auxiliar = 100;
	ident.metodo = MTD_OBTENER_ID;

	(*funcion)(&ident, &resp);

	int idPersona =resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de persona: %i ; idBrokerConectado: %i \n", idPersona, resp->RespuestaRPC_u.datos.id_brokerConectado);

	ident.entidad = TIPO_SALA_ENT;
	ident.numeroEntidad = 1;
	ident.lugar = LUGAR_ENTRADA;
	ident.metodo = MTD_CONSULTAR_ID;

	(*funcion)(&ident, &resp);

	int idSala2 = resp->RespuestaRPC_u.datos.id_entidad;

	if (idSala2 != idSala) {
		printf("Id de sala no es igual 1er: %i; 2do: %i \n", idSala, idSala2);
		errores++;
	}
	else {
		printf("Id de sala es igual, id: %i\n", idSala);
	}


	ident.entidad = TIPO_PERSONA;
	ident.id_auxiliar = 300;
	ident.metodo = MTD_OBTENER_ID;

	(*funcion)(&ident, &resp);

	int idPersona2 =resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de persona: %i ; idBrokerConectado: %i \n", idPersona2, resp->RespuestaRPC_u.datos.id_brokerConectado);


	/*******************************************************************/


	ident.entidad = TIPO_BUS;
	ident.numeroEntidad = 1;
	ident.metodo = MTD_ID_BUS_REEMPLAZO;

	(*funcion)(&ident, &resp);

	idBUS =resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de BUS reemplazo: %i ; idBrokerConectado: %i \n", idBUS, resp->RespuestaRPC_u.datos.id_brokerConectado);


	/*******************************************************************/


	ident.entidad = TIPO_BUS;
	ident.numeroEntidad = 1;
	ident.id_auxiliar = idBUS;
	ident.metodo = MTD_REEMPLAZAR_BUS;

	(*funcion)(&ident, &resp);

	idBUS =resp->RespuestaRPC_u.datos.id_entidad;

	printf("Id de BUS REEMPLAZADO: %i ; idBrokerConectado: %i \n", idBUS, resp->RespuestaRPC_u.datos.id_brokerConectado);


}

